package de.fh.ffm.elevator;

/**
 * Person using the elevator
 * 
 * @author harscher <swe.chr@googlemail.com>
 * @author mann <mann.mario@googlemail.com>
 * 
 */
public class Person {

	/**
	 * if null, this Person is not on any Floor
	 */
	private Floor floor;

	/**
	 * The Floor this Person wants to go to
	 */
	private Floor targetFloor;

	/**
	 * if null, this Person is not on any Elevator
	 */
	private Elevator elevator;

	/**
	 * this Person entered a Floor
	 * 
	 * @param floor
	 */
	public void setFloor ( Floor f ) {
		if ( null != f && this != f.getPerson() ) {
			f.setPerson(this);
		}

		this.floor = f;
	}

	/**
	 * get the floor the Person is on at this moment
	 * 
	 * @return floor
	 */
	public Floor getFloor () {
		return this.floor;
	}

	/**
	 * enter an elevator
	 * 
	 * only possible if the elevator can handle one more person and the doors
	 * are open
	 * 
	 * @param elevator
	 * @throws Elevator.IsFullException
	 *             if the capacity of the elevator is reached
	 * @throws Door.DoorClosedException
	 *             if any of the doors are closed
	 */
	public void enter ( Elevator e ) {
		if ( !e.canPersonEnter() ) {
			throw new Elevator.IsFullException();
		}

		checkCanGoThroughDoors(e);

		if ( null != floor ) {
			floor.setPerson(null);
		}

		e.personEntered(this);
		elevator = e;
	}

	/**
	 * indicates if both doors are open
	 * 
	 * @param e
	 *            the elevator which is checked
	 */
	private void checkCanGoThroughDoors ( Elevator e ) {
		if ( !e.getDoor().isOpen() || !e.getFloor().getDoor().isOpen() ) {
			throw new Door.DoorClosedException();
		}
	}

	/**
	 * check if this person is in an elevator
	 * 
	 * @return true if the person is in the elevator; false otherwise;
	 */
	public boolean isInElevator () {
		return null != elevator;
	}

	/**
	 * Exit an Elevator to the Floor the Elevator is at
	 * 
	 * If a Person is on the Floor and wants to enter the Elevator, both Persons
	 * swap places
	 * 
	 * @throws Floor.IsFullException
	 *             if the Floor is occupied
	 */
	public void exit () {
		checkCanGoThroughDoors(elevator);

		Floor f = elevator.getFloor();

		if ( f.canPersonEnter() ) {
			doExitElevator();
		} else {
			Person personOnFloor = f.getPerson();
			if ( null != personOnFloor.getTargetFloor()
					&& f != personOnFloor.getTargetFloor() ) {
				personOnFloor.setFloor(null);

				Elevator e = elevator;
				doExitElevator();
				personOnFloor.enter(e);
			} else {
				throw new Floor.IsFullException();
			}
		}
	}

	/**
	 * Helper function to exit the elevator if all conditions are checked
	 * 
	 * @see exit
	 */
	private void doExitElevator () {
		floor = elevator.getFloor();
		elevator.personLeft(this);
		elevator = null;
	}

	/**
	 * Indicates whether this Person is located on a specific Floor.
	 * 
	 * @param f
	 *            the Floor which is checked on this Person's presence
	 * @return true if this Person is on the Floor; false otherwise
	 */
	public boolean isOnFloor ( Floor f ) {
		return floor == f;
	}

	/**
	 * Set the Floor this Person wants to go to.
	 * 
	 * @param targetFloor
	 */
	public void setTargetFloor ( Floor targetFloor ) {
		this.targetFloor = targetFloor;
	}

	/**
	 * Returns the Floor this Person wants to go to.
	 * 
	 * @return the target Floor
	 */
	public Floor getTargetFloor () {
		return targetFloor;
	}

}
